﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="SettingConfiguration.cs" company="DevDigital">
//   DevDigital
// </copyright>
// --------------------------------------------------------------------------------------------------------------------

namespace Settings.NET
{
    /// <summary>
    /// Setting configuration
    /// </summary>
    public partial class SettingConfiguration : ISettingConfiguration
    {
        /// <summary>
        /// Settings provider factory
        /// </summary>
        private readonly SettingsProviderFactory settingsProviderFactory;

        /// <summary>
        /// The setting key
        /// </summary>
        private readonly string key;

        /// <summary>
        /// The current scope
        /// </summary>
        private string currentScope;

        /// <summary>
        /// Initializes a new instance of the <see cref="SettingConfiguration"/> class.
        /// </summary>
        /// <param name="key">The key.</param>
        /// <param name="settingsProviderFactory">The settings provider factory.</param>
        /// <param name="settingsStorage">The settings storage.</param>
        /// <param name="settingsSerializer">The settings serializer.</param>
        /// <param name="settingsCache">The settings cache.</param>
        internal SettingConfiguration(
            string key,
            SettingsProviderFactory settingsProviderFactory,
            ISettingsStorage settingsStorage,
            ISettingsSerializer settingsSerializer,
            ISettingsCache settingsCache)
        {
            this.key = key;
            this.settingsProviderFactory = settingsProviderFactory;
            this.currentSettingsStorage = settingsStorage;
            this.currentSerializer = settingsSerializer;
            this.currentCache = settingsCache;
        }

        /// <summary>
        /// Sets the current scope
        /// </summary>
        /// <typeparam name="TScope">The type of the scope.</typeparam>
        /// <param name="scope">The scope.</param>
        /// <returns>
        /// The setting configuration
        /// </returns>
        public ISettingConfiguration InScope<TScope>(TScope scope)
        {            
            this.currentScope = ConversionHelper.IsNull(scope) ? null : scope.ToString();
            return this;
        }

        /// <summary>
        /// Sets the current scope
        /// </summary>
        /// <param name="scope">The scope.</param>
        /// <returns>
        /// The setting configuration
        /// </returns>
        public ISettingConfiguration InScope(string scope)
        {
            this.currentScope = scope;
            return this;
        }

        /// <summary>
        /// Sets the current scope to the global scope
        /// </summary>
        /// <returns>
        /// The setting configuration
        /// </returns>
        public ISettingConfiguration InGlobalScope()
        {
            this.currentScope = null;
            return this;
        }

        /// <summary>
        /// Gets the setting information, or the setting information with the specified default value if the setting is not in storage or there is a deserialization error.
        /// </summary>
        /// <typeparam name="TValue">The type of the value.</typeparam>
        /// <param name="defaultValue">The default value.</param>
        /// <returns>
        /// The setting information, or the setting information with the specified default value if the setting is not in storage or there is a deserialization error
        /// </returns>
        public ISetting<TValue> Get<TValue>(TValue defaultValue = default(TValue))
        {
            var setting = this.currentSettingsStorage.GetSetting(this.key, this.currentScope);

            if (setting == null || !setting.IsInStorage)
            {
                return new Setting<TValue>(this.key, defaultValue, false);
            }

            // TODO: add caching if this.currentCache is set
            var deserializedValue = this.currentSerializer.Deserialize<TValue>(setting.Value);
            return new Setting<TValue>(this.key, deserializedValue, true);
        }

        /// <summary>
        /// Gets the setting information, or the setting information with the specified default value if the setting is not in storage or there is a deserialization error.
        /// </summary>
        /// <param name="defaultValue">The default value.</param>
        /// <returns>
        /// The setting information, or the setting information with the specified default value if the setting is not in storage or there is a deserialization error
        /// </returns>
        public ISetting<string> Get(string defaultValue = null)
        {
            return this.Get<string>(defaultValue);
        }

        /// <summary>
        /// Gets the setting value, or the specified default value if the setting is not in storage or there is a deserialization error.
        /// </summary>
        /// <typeparam name="TValue">The type of the value.</typeparam>
        /// <param name="defaultValue">The default value.</param>
        /// <returns>
        /// The setting value, or the specified default value if the setting is not in storage or there is a deserialization error
        /// </returns>
        public TValue GetValue<TValue>(TValue defaultValue = default(TValue))
        {
            return this.Get(defaultValue).Value;
        }

        /// <summary>
        /// Gets the setting value, or the specified default value if the setting is not in storage or there is a deserialization error.
        /// </summary>
        /// <param name="defaultValue">The default value.</param>
        /// <returns>
        /// The setting value, or the specified default value if the setting is not in storage or there is a deserialization error
        /// </returns>
        public string GetValue(string defaultValue = null)
        {
            return this.GetValue<string>(defaultValue);
        }

        /// <summary>
        /// Sets the setting value.
        /// </summary>
        /// <typeparam name="TValue">The type of the value.</typeparam>
        /// <param name="value">The value.</param>
        public void SetValue<TValue>(TValue value = default(TValue))
        {            
            var serializedValue = this.currentSerializer.Serialize(value);
            this.currentSettingsStorage.SetSetting(this.key, serializedValue, this.currentScope);
        }

        /// <summary>
        /// Sets the setting value.
        /// </summary>
        /// <param name="value">The value.</param>
        public void SetValue(string value)
        {
            this.SetValue<string>(value);
        }

        /// <summary>
        /// Removes the setting from storage.
        /// </summary>
        public void Remove()
        {
            this.currentSettingsStorage.RemoveSetting(this.key, this.currentScope);
        }
    }
}